package com.tentact.coolmeetingtest.controller; /**
 * @author 星环
 * @date 2023/1/11 14:25:28
 * @description
 */
import com.tentact.coolmeetingtest.annotation.AutoBuild;
import com.tentact.coolmeetingtest.annotation.CookieParam;
import com.tentact.coolmeetingtest.annotation.RequestParam;
import com.tentact.coolmeetingtest.annotation.SessionParam;
import com.tentact.coolmeetingtest.controller.servlet.MeetingServlet;
import com.tentact.coolmeetingtest.controller.servlet.DeptServlet;
import com.tentact.coolmeetingtest.controller.servlet.EmpServlet;
import com.tentact.coolmeetingtest.controller.servlet.RoomServlet;
import com.tentact.coolmeetingtest.support.MyMessage;
import com.tentact.coolmeetingtest.util.OtherUtil;
import com.tentact.coolmeetingtest.util.StringUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

@WebServlet(name = "MainServlet", value = "/MainServlet")
public class MainServlet extends HttpServlet {

    //servletName -> ServletObj
    private static final Map<String,Object> beanMap = new HashMap<>();
    //servletname -> map {methodname -> method}
    private static final Map<String, Map<String, Method>> map = new HashMap<>();
    //typename -> getterFunction
    private static final Map<String, Function<String,Object>> paramConverterMap = new HashMap<>();

    static {
        loadBeans();
        loadInjectTypeDealer();
    }


    private static void loadBeans() {
        beanMap.put("dept",new DeptServlet());
        beanMap.put("emp",new EmpServlet());
        beanMap.put("room",new RoomServlet());
        beanMap.put("meeting",new MeetingServlet());
    }


    private static void loadInjectTypeDealer() {
        paramConverterMap.put("Integer", StringUtil::parseIntWithNullEmptyCheck);
        paramConverterMap.put("int", StringUtil::parseIntWithNullEmptyCheck);
        paramConverterMap.put("String",param->{
            if("".equals(param)) return null;
            else return param;
        });
        paramConverterMap.put("Timestamp",StringUtil::webTimeToTimestamp);
        paramConverterMap.put("List", param->List.of(param.split(",")));
        //
    }
//可以写成一个json解析器，把json转换成泛型类
    //public static <T> void getObjectWithTrueTypeRec(Parameter parameter){
    //    if(parameter.getParameterizedType())
    //}

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        String destination = request.getParameter("dest");
        //xxx.mmm  : xxxServlet.Methodname
        String servletName = destination.substring(0, destination.indexOf('.'));
        String methodName = destination.substring(servletName.length()+1);
        //取得对应的servlet方法表，或者注册新的servlet方法表
        Map<String, Method> methodMap = map.getOrDefault(servletName,registerAndGetServletMethodMap(servletName));
        //取得方法
        Method method = methodMap.get(methodName);
        Object[] args = getArgs(request,response,method);
        for (Object arg : args) {
            if(arg != null)
                System.out.println(arg.getClass());
        }
        MyMessage message = null;
        try {
            //调用
            Object obj = beanMap.get(servletName);
            message = (MyMessage) method.invoke(obj, args);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        message.params.forEach(request::setAttribute);
        System.out.println("[log] params listed as follow:");
        message.params.forEach((k,v)->System.out.println(k + "->" + v));
        System.out.println("[log] params set,go to dest ->" + message.dispatchDest);
        if(message.dispatchDest == null){
            return;
        }
        if(message.isDestFunc){
            message.dispatchDest = "MainServlet?dest=" + message.dispatchDest;
        }
        try {
            if(message.dispatchDest.startsWith("search")){
                System.out.println(request.getParameter("now_page"));
            }
            if(message.dispatchDest != null)
                request.getRequestDispatcher(message.dispatchDest).forward(request,response);
        } catch (ServletException | IOException e) {
            throw new RuntimeException(e);
        }
    }

    private Object[] getArgs(HttpServletRequest request,HttpServletResponse response,Method method) {
        Parameter[] parameters = method.getParameters();
        List<Object> list = new ArrayList<>();
        for (Parameter parameter : parameters) {
            Class<?> type = parameter.getType();
            if (HttpServlet.class.equals(type)) {
                list.add(this);
                continue;
            } else if (HttpServletRequest.class.equals(type)) {
               list.add(request);
               continue;
            } else if (HttpServletResponse.class.equals(type)) {
                list.add(response);
                continue;
            }

            Annotation vo = type.getAnnotation(AutoBuild.class);
            Object o = null;

            o = getNormalArg(request,parameter);
            if(o == null){
                if(type.getAnnotation(AutoBuild.class) != null) {
                    //实体类
                    System.out.println(type.getName());
                    o = getVoArg(request,type);
                }
            }
            list.add(o);
        }
        return list.toArray();
    }

    /**
     * 取得vo
     * @param request
     * @param type
     * @return
     */
    private Object getVoArg(HttpServletRequest request, Class<?> type) {
        Method[] methods = type.getMethods();
        Object o = null;
        try {
            o = type.getConstructor(null).newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
        try {
            //注入所有参数
            for (Method method : methods) {
                if(method.getName().startsWith("set")){
                    String requestParameter = request.getParameter(StringUtil.setterToSQLName(method.getName()));
                    if(requestParameter != null){
                        injectParamToObjectBySetter(requestParameter,o,method);
                    }
                }
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        return o;
    }


    private void injectParamToObjectBySetter(String param, Object o, Method setter) throws InvocationTargetException, IllegalAccessException {
        Class<?> parameterType = setter.getParameterTypes()[0];
        String type = parameterType.getSimpleName();
        if(parameterType.getName().equals("int")){
            type = "Integer";
        }
        System.out.println(type);
        Function<String, ?> stringFunction = paramConverterMap.get(type);
        Object apply = stringFunction.apply(param);
        if (apply == null){
            System.out.println("[info]:got null on injecting param-" + param + "-type is " + parameterType.getName()+",prim value is "+ param);
        }
        setter.invoke(o, apply);
    }

    /**
     * 普通参数
     *
     * @param request
     * @param param
     * @return
     */
    private Object getNormalArg(HttpServletRequest request, Parameter param) {
        if(param.getAnnotation(RequestParam.class) != null){
            String key = param.getAnnotation(RequestParam.class).value();
            return paramConverterMap.get(param.getType().getSimpleName()).apply(request.getParameter(key));
        }
        if(param.getAnnotation(SessionParam.class) != null){
            return request.getSession().getAttribute(param.getAnnotation(SessionParam.class).value());
        }
        if(param.getAnnotation(CookieParam.class) != null){
            String key = param.getAnnotation(CookieParam.class).value();
            Cookie[] cookies = request.getCookies();

            String cookieStr = OtherUtil.getFromCookies(key, cookies);
            return paramConverterMap.get(param.getType().getSimpleName()).apply(cookieStr);
        }
        return null;
    }

    private Map<String, Method> registerAndGetServletMethodMap(String servletName) {
        //类名QqqServlet
        String servletSimpleName = Character.toUpperCase(servletName.charAt(0)) + servletName.substring(1) + "Servlet";
        try {
            //完整类名取得类
            Class<?> aClass = Class.forName("com.tentact.coolmeetingtest.controller.servlet." + servletSimpleName);
            //取得所有Method
            Method[] methods = aClass.getMethods();
            //取得methodName->method ，只获取公有方法，有不想被注册的方法可设为私有
            Map<String,Method> methodMap = new HashMap<>();
            for (Method method : methods) {
                String name = method.getName();
                methodMap.put(name,method);
            }
            map.put(servletName,methodMap);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        return map.get(servletName);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        doGet(request,response);
    }

}
